#ifndef COMPILER_OPTIMIZATIONS_H
#define COMPILER_OPTIMIZATIONS_H

#include "Basic.h"
#include "CFG.h"

namespace Compiler
{
	namespace Intermediate
	{
		using namespace CoreLib::Basic;

		struct ProgramOptimizationResult
		{
			RefPtr<ControlFlowGraph> Program;
			bool Changed;
			ProgramOptimizationResult()
			{
				Changed = false;
			}
		};

		class IntraProcOptimizer : public Object
		{
		public:
			virtual ProgramOptimizationResult Optimize(RefPtr<ControlFlowGraph> program)=0;
		};
		
		class CompoundOptimizer : public IntraProcOptimizer
		{
		public:
			List<RefPtr<IntraProcOptimizer>> Optimizers;
			CompoundOptimizer()
			{}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1)
			{
				Optimizers.Add(op1);
			}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1, RefPtr<IntraProcOptimizer> op2)
			{
				Optimizers.Add(op1);
				Optimizers.Add(op2);
			}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1, RefPtr<IntraProcOptimizer> op2, RefPtr<IntraProcOptimizer> op3)
			{
				Optimizers.Add(op1);
				Optimizers.Add(op2);
				Optimizers.Add(op3);
			}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1, RefPtr<IntraProcOptimizer> op2, RefPtr<IntraProcOptimizer> op3, RefPtr<IntraProcOptimizer> op4)
			{
				Optimizers.Add(op1);
				Optimizers.Add(op2);
				Optimizers.Add(op3);
				Optimizers.Add(op4);
			}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1, RefPtr<IntraProcOptimizer> op2, RefPtr<IntraProcOptimizer> op3, RefPtr<IntraProcOptimizer> op4, RefPtr<IntraProcOptimizer> op5)
			{
				Optimizers.Add(op1);
				Optimizers.Add(op2);
				Optimizers.Add(op3);
				Optimizers.Add(op4);
				Optimizers.Add(op5);
			}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1, RefPtr<IntraProcOptimizer> op2, RefPtr<IntraProcOptimizer> op3, RefPtr<IntraProcOptimizer> op4, RefPtr<IntraProcOptimizer> op5, RefPtr<IntraProcOptimizer> op6)
			{
				Optimizers.Add(op1);
				Optimizers.Add(op2);
				Optimizers.Add(op3);
				Optimizers.Add(op4);
				Optimizers.Add(op5);
				Optimizers.Add(op6);
			}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1, RefPtr<IntraProcOptimizer> op2, RefPtr<IntraProcOptimizer> op3, RefPtr<IntraProcOptimizer> op4, RefPtr<IntraProcOptimizer> op5, RefPtr<IntraProcOptimizer> op6, RefPtr<IntraProcOptimizer> op7)
			{
				Optimizers.Add(op1);
				Optimizers.Add(op2);
				Optimizers.Add(op3);
				Optimizers.Add(op4);
				Optimizers.Add(op5);
				Optimizers.Add(op6);
				Optimizers.Add(op7);
			}
			CompoundOptimizer(RefPtr<IntraProcOptimizer> op1, RefPtr<IntraProcOptimizer> op2, RefPtr<IntraProcOptimizer> op3, RefPtr<IntraProcOptimizer> op4, RefPtr<IntraProcOptimizer> op5, RefPtr<IntraProcOptimizer> op6, RefPtr<IntraProcOptimizer> op7, RefPtr<IntraProcOptimizer> op8)
			{
				Optimizers.Add(op1);
				Optimizers.Add(op2);
				Optimizers.Add(op3);
				Optimizers.Add(op4);
				Optimizers.Add(op5);
				Optimizers.Add(op6);
				Optimizers.Add(op7);
				Optimizers.Add(op8);
			}
		
			virtual ProgramOptimizationResult Optimize(RefPtr<ControlFlowGraph> program) override
			{
				ProgramOptimizationResult rs;
				rs.Program = program;
				for (auto & optimizer : Optimizers)
				{
					auto partialRs = optimizer->Optimize(rs.Program);
					rs.Program = partialRs.Program;
					rs.Changed |= partialRs.Changed;
				}
				return rs;
			}
		};

		class IterateOptimizer : public IntraProcOptimizer
		{
		public:
			RefPtr<IntraProcOptimizer> Optimizer;
			IterateOptimizer()
			{}
			IterateOptimizer(RefPtr<IntraProcOptimizer> optimizer)
			{
				this->Optimizer = optimizer;
			}
			virtual ProgramOptimizationResult Optimize(RefPtr<ControlFlowGraph> program) override
			{
				bool changed = false;
				ProgramOptimizationResult rs;
				rs.Program = program;
				do
				{
					rs = Optimizer->Optimize(rs.Program);
					if (rs.Changed)
						changed = true;
				}
				while (rs.Changed);
				rs.Changed = changed;
				return rs;
			}
		};
		
		IntraProcOptimizer * CreateDeadCodeOptimizer();
		IntraProcOptimizer * CreateUselessInstructionOptimizer();
		IntraProcOptimizer * CreateVariableCleanupOptimizer();
		IntraProcOptimizer * CreateConstIndirectionRemovalOptimizer();
		IntraProcOptimizer * CreateOutOfSSA_Transform();
		IntraProcOptimizer * CreateRegisterAllocator();
		IntraProcOptimizer * CreateBranchFuseOptimizer();
		IntraProcOptimizer * CreateControlFlowCleanupOptimizer();
		IntraProcOptimizer * CreatePeepHoleOptimizer();
	}
}

#endif